package cn.com.chengmandian.bike.controller;

import cn.com.chengmandian.agent.utils.AuthUtil;
import cn.com.chengmandian.agent.utils.ServletUtil;
import cn.com.chengmandian.bike.dto.*;
import cn.com.chengmandian.bike.log.ActionLog;
import cn.com.chengmandian.bike.model.*;
import cn.com.chengmandian.bike.model.vo.LoginUserVo;
import cn.com.chengmandian.bike.model.vo.OpenGetMemberInfoVo;
import cn.com.chengmandian.bike.pojo.UserCodeInfo;
import cn.com.chengmandian.bike.service.*;
import cn.com.chengmandian.bike.utils.AliPayUtil;
import cn.com.chengmandian.bike.utils.WxPayUtil;
import cn.com.chengmandian.core.common.constants.CommonConstant;
import cn.com.chengmandian.core.common.constants.ResponseCode;
import cn.com.chengmandian.core.redis.cache.AgentParamCacheService;
import cn.com.chengmandian.core.redis.service.RedisService;
import cn.com.chengmandian.core.web.exceptions.BadResponseException;
import cn.com.chengmandian.core.web.pojo.*;
import cn.com.chengmandian.core.web.utils.LocalMapUtil;
import cn.com.chengmandian.service.bike.BikeCommonClient;
import cn.com.chengmandian.service.bike.BikePayClient;
import cn.com.chengmandian.service.bike.constants.ChannelTypeEnum;
import cn.com.chengmandian.service.bike.constants.MemberFaceBusinessType;
import cn.com.chengmandian.service.bike.constants.MemberPayBusinessTypeEnum;
import cn.com.chengmandian.service.bike.constants.PayChannelEnum;
import cn.com.chengmandian.service.bike.form.BikePayForm;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;

@Slf4j
@Api(tags = "用户相关接口")
@RestController
@RequestMapping("/member/info")
public class EbMemberInfoController {

    @Autowired
    private IEbMemberInfoService memberInfoService;

    @Autowired
    private IEbMemberWalletService memberWalletService;

    @Autowired
    private IEbDepositConfigService depositConfigService;

    @Autowired
    private IEbMemberPayService memberPayService;

    @Autowired
    private IEbMemberDepositService memberDepositService;

    @Autowired
    private AgentParamCacheService agentParamCacheService;

    @Autowired
    private BikeCommonClient bikeCommonClient;

    @Autowired
    private RedisService redisService;

    @Autowired
    private IEbOperationAreaService operationAreaService;

    @Autowired
    private IEbSettingArticleService settingArticleService;

    @Autowired
    private IEbMemberFaceService memberFaceService;

    @Autowired
    private IEbAgentInfoService agentInfoService;

    @Autowired
    private IEbPayAccountService payAccountService;

    @Autowired
    private BikePayClient payClient;

    @ApiOperation("根据code授权外部系统获取用户信息")
    @GetMapping("/open/get")
    public AjaxResult<OpenGetMemberInfoVo> open(String code) {
        Assert.isTrue(StrUtil.isNotEmpty(code), "缺少code");
        String key = "CMD:BIKE:MEMBER:OPEN:" + code;
        Object v = redisService.get(key);
        if (v == null) {
            return AjaxResult.error("不存在的code");
        }
        redisService.del(key);
        OpenGetMemberInfoVo memberInfoVo = (OpenGetMemberInfoVo)v;
        return AjaxResult.success(memberInfoVo);
    }

    @ApiOperation("生成对外部系统用户授权信息")
    @PostMapping("/open/add")
    public AjaxResult open() {
        AgentUser agentUser = AuthUtil.getAgentUser();
        EbMemberInfo memberInfo = memberInfoService.getById(agentUser.getId());
        OpenGetMemberInfoVo memberInfoVo = new OpenGetMemberInfoVo();
        memberInfoVo.setUuid(MD5.create().digestHex("EB" + agentUser.getAgentId() + "-" + memberInfo.getId()));
        memberInfoVo.setName(memberInfo.getName());
        memberInfoVo.setMobile(memberInfo.getMobile());
        memberInfoVo.setSex(memberInfoVo.getSex());

        AgentInfo agentInfo = LocalMapUtil.getAgentInfo();
        if (agentInfo.getAreaId() != null) {
            EbOperationArea operationArea = operationAreaService.getById(agentInfo.getAreaId());
            if (operationArea != null) {
                memberInfoVo.setAreaName(operationArea.getName());
            }
        }

        String code = RandomUtil.randomString(32);
        String key = "CMD:BIKE:MEMBER:OPEN:" + code;
        redisService.set(key, memberInfoVo, 60 * 10);
        return AjaxResult.success(code);
    }

    @ApiOperation("获取已登陆用户信息")
    @GetMapping("/get")
    public AjaxResult<LoginUserVo> info() {
        AgentUser agentUser = AuthUtil.getAgentUser();
        LoginUserVo loginUserVo = new LoginUserVo();
        EbMemberInfo memberInfo = memberInfoService.getById(agentUser.getId());
        if(memberInfo == null || memberInfo.getIsDeleted() == 1 || memberInfo.getIsEnabled() == 0 || memberInfo.getIsOff() == 1){
            throw new BadResponseException(ResponseCode.TOKEN_INVALID,"请先登录");
        }
        BeanUtils.copyProperties(memberInfo, loginUserVo);
        AgentInfo agentInfo = LocalMapUtil.getAgentInfo();

        if (StrUtil.isBlank(memberInfo.getNickname())) {
            loginUserVo.setNeedUserInfo(true);
        }

//        BikeAreaParam bikeAreaParam = agentParamCacheService.getParam(agentInfo.getId(), agentInfo.getAreaId());
        BikeParam bikeParam = agentParamCacheService.getParam(agentInfo.getId());
        if (StrUtil.isBlank(memberInfo.getIdCard()) || StrUtil.isBlank(memberInfo.getName())) {
            // 如果没有人工认证
            if (memberInfo.getManualAuth() == 0) {
                loginUserVo.setNeedAuth(true);
            }
        }

        // 关闭了实名认证信息
        if(bikeParam != null && !bikeParam.getUserAgentRideRealNameAuthEnabled()) {
            loginUserVo.setNeedAuth(false);
        }

        loginUserVo.setMemberWallet(memberWalletService.getMemberWallet(agentUser.getId()));

        if (agentInfo.getAreaId() != null) {
            // 免押卡信息
            EbMemberDeposit memberDeposit = memberDepositService.getMemberAvailableRecord(agentInfo.getAreaId(), agentUser.getId());
            if (memberDeposit != null) {
                loginUserVo.setDepositCardExpireTime(memberDeposit.getExpireTime());
            }
            if(bikeParam != null && bikeParam.getUserAgentMiniLoginFaceAuthEnabled()){
                loginUserVo.setIsNeedFace(true);
            }else {
                loginUserVo.setIsNeedFace(false);
            }
        }
        // 用户协议
        EbSettingArticle ebSettingArticle = settingArticleService.getByType(CommonConstant.ONE);
        if(ObjectUtil.isNotNull(ebSettingArticle)){
            Integer isAgree = CommonConstant.ZERO;
            if(memberInfo.getAgreementVersion().equals(ebSettingArticle.getVersion())){
                isAgree = CommonConstant.ONE;
            }
            loginUserVo.setIsAgree(isAgree);
        }else{
            loginUserVo.setIsAgree(CommonConstant.ONE);
        }
        loginUserVo.setIdCard(memberInfo.getIdCard());
        loginUserVo.setIsFace(memberInfo.getIsFace());
        return AjaxResult.success(loginUserVo);
    }

    @ApiOperation("提交实名信息")
    @PostMapping("/auth")
    @ActionLog(name="提交实名信息",description = "'参数：' + #memberAuthForm")
    public AjaxResult auth(@RequestBody MemberAuthForm memberAuthForm) {
        Assert.isTrue(StrUtil.isNotBlank(memberAuthForm.getName()), "请填写真实姓名!");
        Assert.isTrue(StrUtil.isNotBlank(memberAuthForm.getIdCard()), "请填写身份证号!");
        Assert.isTrue(IdcardUtil.isValidCard(memberAuthForm.getIdCard()), "身份证格式不正确");
        Assert.isTrue(IdcardUtil.getAgeByIdCard(memberAuthForm.getIdCard()) >= 16, "需要年满16周岁");

        AgentUser agentUser = AuthUtil.getAgentUser();
        AgentInfo agentInfo = LocalMapUtil.getAgentInfo();
        BikeParam bikeParam = agentParamCacheService.getParam(agentInfo.getId());

        //是否存在身份证号姓名冲突
        if(memberInfoService.isExistThisIdCard(agentUser.getId(), memberAuthForm.getIdCard())){
            return AjaxResult.error("身份证号已与其他账号绑定，请联系客服");
        }

        Integer isFace = CommonConstant.ZERO;
        // 人脸识别认证
        if(bikeParam.getUserAgentMiniLoginFaceAuthEnabled()) {
            EbMemberInfo memberInfo = memberInfoService.getById(agentUser.getId());
            // 1小时内禁止重复认证(注册认证)
            if(memberInfo.getAuthPassTime() != null && DateUtil.between(memberInfo.getAuthPassTime(), DateUtil.date(), DateUnit.HOUR) <= 1) {
                throw new BadResponseException("1小时内禁止重复认证");
            }
            Assert.isTrue(StrUtil.isNotBlank(memberAuthForm.getFacePhotoBase64()) || StrUtil.isNotBlank(memberAuthForm.getFacePhotoUrl()), "缺少认证图像信息");

            memberFaceService.userFaceAuth(memberAuthForm.getName(),
                memberAuthForm.getIdCard(),
                memberAuthForm.getFacePhotoBase64(),
                memberAuthForm.getFacePhotoUrl(),
                memberInfo.getId(),
                memberInfo.getMobile(),
                agentInfo.getFaceAppcode(),
                MemberFaceBusinessType.REGISTER.getCode());

            isFace = CommonConstant.ONE;
        }
        memberInfoService.memberAuth(agentUser.getId(), memberAuthForm.getName(), memberAuthForm.getIdCard(), isFace);
        return AjaxResult.success("认证成功");
    }

    @ApiOperation("实名注册信息(无需登录)")
    @PostMapping("/auth/register")
    @ActionLog(name="注册前实名",description = "'参数：' + #form")
    public AjaxResult registerAuth(@RequestBody AuthRegisterForm form) {
        Assert.isTrue(StrUtil.isNotBlank(form.getUserCode()), "缺少用户临时标识!");
        Assert.isTrue(StrUtil.isNotBlank(form.getName()), "请填写真实姓名!");
        Assert.isTrue(StrUtil.isNotBlank(form.getIdCard()), "请填写身份证号!");
        Assert.isTrue(IdcardUtil.isValidCard(form.getIdCard()), "身份证格式不正确");
        Assert.isTrue(IdcardUtil.getAgeByIdCard(form.getIdCard()) >= 16, "需要年满16周岁");
        AgentInfo agentInfo = LocalMapUtil.getAgentInfo();
        String key = "CMD:BIKE:"+ agentInfo.getId() + ":USERCODE:" + form.getUserCode();
        Assert.isTrue(redisService.hasKey(key), "缺少注册信息");
        BikeParam bikeParam = agentParamCacheService.getParam(agentInfo.getId());

        //是否存在身份证号姓名冲突
        if(memberInfoService.isExistThisIdCard(null, form.getIdCard())){
            return AjaxResult.error("身份证号已与其他账号绑定，请联系客服");
        }
        UserCodeInfo userCodeInfo = (UserCodeInfo) redisService.get(key);

        Integer isFace = CommonConstant.ZERO;
        // 人脸识别认证
        if(bikeParam.getUserAgentMiniLoginFaceAuthEnabled()) {
            Assert.isTrue(StrUtil.isNotBlank(form.getFacePhotoBase64()) || StrUtil.isNotBlank(form.getFacePhotoUrl()), "缺少认证图像信息");

            memberFaceService.userFaceAuth(form.getName(),
                form.getIdCard(),
                form.getFacePhotoBase64(),
                form.getFacePhotoUrl(),
                null,
                    userCodeInfo.getMobile(),
                agentInfo.getFaceAppcode(),
                MemberFaceBusinessType.REGISTER.getCode());

            isFace = CommonConstant.ONE;
        }

        // 再次检查，防止重复
        EbMemberInfo memberInfo = memberInfoService.getByPhone(userCodeInfo.getMobile());
        if (memberInfo == null) {
            memberInfo = memberInfoService.createMember(agentInfo.getAreaId(),
                    userCodeInfo.getMobile(),
                    userCodeInfo.getOpenId(),
                    userCodeInfo.getAliPayUserId(),
                    userCodeInfo.getAliPayOpenId());
        } else {
            log.error("实名后注册用户已存在用户信息：{}", JSONObject.toJSONString(userCodeInfo));
        }
        memberInfoService.memberAuth(memberInfo.getId(), form.getName(), form.getIdCard(), isFace);
        String token = memberInfoService.getToken(memberInfo);
        redisService.del(key);
        return AjaxResult.success(MapUtil.of("token", token));
    }

    @ApiOperation("骑行认证")
    @PostMapping("/ride/auth")
    public AjaxResult rideAuth(@RequestBody MemberAuthForm form) {
        AgentUser agentUser = AuthUtil.getAgentUser();
        AgentInfo agentInfo = LocalMapUtil.getAgentInfo();
        BikeAreaParam bikeAreaParam = agentParamCacheService.getParam(agentInfo.getId(), agentInfo.getAreaId());
        if(bikeAreaParam.getUserRideFaceAuthEnabled()) {
            EbMemberInfo memberInfo = memberInfoService.getById(agentUser.getId());
            memberFaceService.userFaceAuth(memberInfo.getName(), memberInfo.getIdCard(), form.getFacePhotoBase64(), form.getFacePhotoUrl(), memberInfo.getId(), memberInfo.getMobile(), agentInfo.getFaceAppcode(), MemberFaceBusinessType.RIDE.getCode());
        }
        return AjaxResult.success();
    }

    @ApiOperation("完善用户基本信息")
    @PostMapping("/update")
    public AjaxResult update(@RequestBody MemberInfoUpdateForm memberInfoUpdateForm) {
        AgentUser agentUser = AuthUtil.getAgentUser();
        memberInfoService.updateInfo(agentUser.getId(), memberInfoUpdateForm);
        return AjaxResult.success("更新成功");
    }

    @ApiOperation("支付押金")
    @PostMapping("/deposit")
    @ActionLog(name="支付押金",description = "'参数：' + #memberDepositPayForm")
    public AjaxResult deposit(@RequestBody MemberDepositPayForm memberDepositPayForm) throws Exception{
        Assert.notNull(memberDepositPayForm.getPayChannel(), "请选择充值渠道");
        Integer memberId = AuthUtil.getAgentUser().getId();
//        if(!SpringContextUtil.isDev()) {
//            Assert.isTrue(false, "无法缴纳押金，请联系客服");
//        }

        AgentInfo agentInfo = LocalMapUtil.getAgentInfo();
        Assert.notNull(agentInfo.getAreaId(), "缺少运营区");
        EbDepositConfig depositConfig = depositConfigService.getConfig(agentInfo.getAreaId());
        if (depositConfig.getDepositMoney().compareTo(new BigDecimal(0)) == 0) {
            return AjaxResult.error("您可以免押金骑行");
        }
        if (depositConfig.getIsForce().equals(CommonConstant.ONE)) {
            return AjaxResult.error("系统强制要求购买免押会员");
        }
        if (memberDepositPayForm.getPayChannel().equals(PayChannelEnum.WALLET.getCode())) {
            EbMemberWallet memberWallet = memberWalletService.getMemberWallet(memberId);
            if (memberWallet.getChargeMoney().compareTo(depositConfig.getDepositMoney()) < 0) {
                return AjaxResult.error("钱包充值余额不足");
            }
        }

        EbMemberInfo memberInfo = memberInfoService.getById(memberId);
        Assert.isTrue(memberInfo.getIsDeposit().equals(CommonConstant.ZERO), "您已缴纳押金，请勿重复缴纳");

        EbAgentInfo info = agentInfoService.getById(agentInfo.getId());
        if (info.getPayAccountId() != null) {
            EbPayAccount payAccount = payAccountService.getPayAccount(info.getPayAccountId(), memberId);
            if (payAccount != null && (memberDepositPayForm.getPayChannel().equals(PayChannelEnum.WXPAY.getCode()) || memberDepositPayForm.getPayChannel().equals(PayChannelEnum.ALIPAY.getCode()))) {
                PayChannelEnum payChannel = PayChannelEnum.getByCode(memberDepositPayForm.getPayChannel());
                BikePayForm bikePayForm = new BikePayForm();
                bikePayForm.setPayBusinessType(MemberPayBusinessTypeEnum.DEPOSIT);
                bikePayForm.setPayChannel(payChannel);
                bikePayForm.setMoney(depositConfig.getDepositMoney());
                bikePayForm.setOpenId(StrUtil.isNotBlank(memberDepositPayForm.getOpenId()) ? memberDepositPayForm.getOpenId() : memberInfo.getOpenId());
                bikePayForm.setAlipayUserId(memberInfo.getAlipayUserId());
                bikePayForm.setAlipayOpenId(memberInfo.getAlipayOpenId());
                bikePayForm.setAreaId(agentInfo.getAreaId());
                bikePayForm.setMemberId(AuthUtil.getAgentUser().getId());
                bikePayForm.setClientIp(ServletUtil.getIp());
                return payClient.pay(agentInfo.getId(), bikePayForm);
            }
        }

        EbMemberPay memberPay = memberPayService.createPay(agentInfo.getAreaId(), AuthUtil.getAgentUser().getId(), MemberPayBusinessTypeEnum.DEPOSIT, null,
                depositConfig.getDepositMoney(), memberDepositPayForm.getPayChannel());

        String notifyUrl = "http://"+ agentInfo.getDomain() + "/api/v1/bikeuser/notify";

        if (memberDepositPayForm.getPayChannel().equals(PayChannelEnum.WXPAY.getCode())) {
            Assert.isTrue(StrUtil.isNotBlank(agentInfo.getWxMchId()), "未开启微信支付");
            if (memberDepositPayForm.getChannelType().equals(ChannelTypeEnum.MINI.getCode())) {
                String openId = StrUtil.isNotBlank(memberDepositPayForm.getOpenId()) ? memberDepositPayForm.getOpenId() : memberInfo.getOpenId();
                Assert.isTrue(StrUtil.isNotBlank(openId), "缺少openid");
                return AjaxResult.success(WxPayUtil.jsapiTransactions(
                        agentInfo.getWxMiniAppid(),
                        agentInfo.getWxMchId(),
                        openId,
                        "缴纳押金",
                        memberPay.getPayNo(),
                        memberPay.getMoney(),
                        agentInfo.getWxKeyPath(),
                        agentInfo.getWxCertPath(),
                        notifyUrl + "/wxpay/pay/" + LocalMapUtil.getAgentInfo().getAgentNo()
                ));
            }
        } else if (memberDepositPayForm.getPayChannel().equals(ChannelTypeEnum.MINI.getCode())) {
            // 支付宝用户唯一标识user_id 和open_id必须有一个有值
            Assert.isTrue(StrUtil.isNotBlank(memberInfo.getAlipayUserId()) || StrUtil.isNotBlank(memberInfo.getAlipayOpenId()), "用户缺少支付宝用户标识");
            return AjaxResult.success(AliPayUtil.getTradeCreateRequestParam(
                    agentInfo.getAliMiniAppid(),
                    memberInfo.getAlipayUserId(),
                    memberInfo.getAlipayOpenId(),
                    "缴纳押金",
                    memberPay.getPayNo(),
                    memberPay.getMoney(),
                    agentInfo.getAliPayPrivateKey(),
                    agentInfo.getAliPayCertPath(),
                    agentInfo.getAliPayPublicCertPath(),
                    agentInfo.getAliPayRootCertPath(),
                    notifyUrl + "/ali/pay/" + LocalMapUtil.getAgentInfo().getAgentNo()
            ));
        } else if (memberDepositPayForm.getPayChannel().equals(PayChannelEnum.WALLET.getCode())) {
            memberPayService.paySuccessByPay(memberPay, null);
            return AjaxResult.success("购买成功");
        }
        return AjaxResult.error("不支持的支付方式！");
    }

    @ApiOperation("同意用户协议")
    @PostMapping("/version")
    public AjaxResult version(){
        memberInfoService.version(AuthUtil.getAgentUser().getId());
        return AjaxResult.success();
    }

    @ApiOperation("账号注销")
    @PostMapping("/logout")
    public AjaxResult logout(@RequestBody MemberLogoutForm form){
        Assert.isTrue(StrUtil.isNotBlank(form.getReason()), "请输入注销原因");
        AgentUser agentUser = AuthUtil.getAgentUser();
        Assert.notNull(agentUser, "请先登录");

        memberInfoService.memberLogout(agentUser.getId(), form.getReason());
        return AjaxResult.success();
    }
}
